package org.KeyCrafter.key;

import org.KeyCrafter.cmd.StringCommand;
import org.KeyCrafter.obj.KeyCrafterObjFactory;
import org.KeyCrafter.obj.KeyCrafterObject;
import org.KeyCrafter.obj.StringObj;
import org.KeyCrafter.server.session.ClientSession;
import org.KeyCrafter.util.SharedReply;

import static org.KeyCrafter.server.ServerConstants.*;
import static org.KeyCrafter.util.ReplyUtil.*;

public class StringKey implements StringCommand {
    public final static int SET_NO_FLAGS = 0;
    public final static int SET_NX = 1 << 0;
    public final static int SET_XX = 1 << 1;

    @Override
    public void setCommand(ClientSession c) {
        String expire = null;
        int unit = UNIT_SECONDS;
        int flags = SET_NO_FLAGS;
        // 设置选项参数
        for (int j = 3; j < c.argc(); j++) {
            char[] a = c.argv(j).toCharArray();
            String next = (c.argc() == j - 1) ? null : c.argv(j + 1);
            if ((a[0] == 'n' || a[0] == 'N') &&
                    (a[1] == 'x' || a[1] == 'X')) {
                flags |= SET_NX;
            } else if ((a[0] == 'x' || a[0] == 'X') &&
                    (a[1] == 'x' || a[1] == 'X')) {
                flags |= SET_XX;
            } else if ((a[0] == 'e' || a[0] == 'E') &&
                    (a[1] == 'x' || a[1] == 'X') && next != null) {
                unit = UNIT_SECONDS;
                expire = next;
                j++;
            } else if ((a[0] == 'p' || a[0] == 'P') &&
                    (a[1] == 'x' || a[1] == 'X') && next != null) {
                unit = UNIT_MILLISECONDS;
                expire = next;
                j++;
            } else {
                c.setReplyMessage(SharedReply.syntaxErr);
                return;
            }
        }
        // 对 value 编码
        StringObj value = KeyCrafterObjFactory.newStringObj(c.argv(2));
        setGenericCommand(c, flags, c.argv(1), value, expire, unit, null, null);
    }

    private void setGenericCommand(ClientSession c, int flags, String key, KeyCrafterObject value, String expire, int unit, String okReply, String abortReply) {
        long milliseconds = 0;
        // 取出过期时间
        if (expire != null) {
            try {
                milliseconds = Long.parseLong(expire);
            } catch (NumberFormatException e) {
                addReplyError(c, "value is not an integer or out of range");
            }
            // expire 参数的值不正确时报错
            if (milliseconds <= 0) {
                addReplyError(c, "invalid expire time in SETEX");
                return;
            }
            // 不论输入的过期时间是秒还是毫秒
            // 实际都以毫秒的形式保存过期时间
            // 如果输入的过期时间为秒，那么将它转换为毫秒
            if (unit == UNIT_SECONDS) {
                milliseconds *= 1000;
            }
        }
        if (((flags & SET_NX) != 0 && c.db().lookupKeyWrite(key) != null) ||
                ((flags & SET_XX) != 0 && c.db().lookupKeyWrite(key) == null)) {
            if (abortReply != null) {
                addReply(c, abortReply);
            } else {
                addReply(c, SharedReply.nullBulk);
            }
            return;
        }
        // 将键值关联到数据库
        c.db().setKey(key, value);
        // 将数据库设为脏
        c.db().incrDirty();
        // 为键设置过期时间
        if (expire != null) {
            c.db().setExpire(key, System.currentTimeMillis() + milliseconds);
        }
        if (okReply != null) {
            addReply(c, okReply);
        } else {
            addReply(c, SharedReply.ok);
        }
    }
    // get命令实现
    public void getCommand(ClientSession c) {
        getGenericCommand(c);
    }
    private int getGenericCommand(ClientSession c){
        KeyCrafterObject o;
        // 从数据库中取出键 c ->argv[1] 对应值对象，不存在返回null
        if ((o = c.db().lookupKeyReadOrReply(c, c.argv(1), SharedReply.nullBulk)) == null) {
            return KeyCrafter_OK;
        }
        // 值对象存在，检查它的类型
        if (!(o instanceof StringObj)) {
            // 类型错误
            addReply(c, SharedReply.wrongTypeErr);
            return KeyCrafter_ERR;
        } else {
            // 类型正确，向客户端返回对象的值
            addReplyBulk(c, o);
            return KeyCrafter_OK;
        }
    }
}
